package impl

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	"golang.org/x/term"
)

// Logger 日志的底层存储
type Logger struct {
	L     sync.Mutex
	Color bool
	Out   io.Writer
	// Debug     bool
	Timestamp bool
	Quiet     bool
	Buf       ColorBuffer
	Depth     int // runtime.Caller() 的深度
	LogLevel  int
}

// NewLogger 返回一个输出到终端的自动染色的日志实例
func NewLogger() *Logger {
	return &Logger{
		L:         sync.Mutex{},
		Color:     term.IsTerminal(int(os.Stdout.Fd())),
		Out:       os.Stdout,
		Timestamp: true,
		Depth:     2,
	}
}

// SetLogLevel 设置日志等级
func (l *Logger) SetLogLevel(level int) {
	l.L.Lock()
	defer l.L.Unlock()
	l.LogLevel = level
}

// SetOutput 设置日志输出位置
func (l *Logger) SetOutput(w io.Writer) {
	l.L.Lock()
	defer l.L.Unlock()
	l.Color = false
	if fdw, ok := w.(FdWriter); ok {
		l.Color = term.IsTerminal(int(fdw.Fd()))
	}
	l.Out = w
}

// 实现gorm的日志输出接口
// gorm.io/gorm/logger/logger.go#Writer
func (l *Logger) Printf(s string, args ...interface{}) {
	l.Output(4, InfoPrefix, fmt.Sprintf(s, args...))
}

// Output 输出实际数据
func (l *Logger) Output(depth int, prefix Prefix, data string) error {
	// 共享缓冲区上锁
	l.L.Lock()
	defer l.L.Unlock()

	// 重置缓冲区
	l.Buf.Reset()
	
	// 前缀
	if l.Color {
		l.Buf.Append(prefix.colorPlain)
	} else {
		l.Buf.Append(prefix.Plain)
	}
	
	// 时间戳
	if l.Timestamp {
		l.outputTimestamp()
	}

	// 调用
	if prefix.File {
		l.outputCaller(depth, prefix.ShortFile, prefix.ShowFunc)
	}

	// 打印实际数据
	if l.Color {
		l.Buf.Append(prefix.ColorData([]byte(data)))
	} else {
		l.Buf.Append([]byte(data))
	}
	
	// 换行符
	if len(data) == 0 || data[len(data)-1] != '\n' {
		l.Buf.AppendByte('\n')
	}
	// 冲洗缓冲区内的数据到输出位置
	_, err := l.Out.Write(l.Buf.Buffer)
	return err
}

// 时间戳染色
func (l *Logger) outputTimestamp() {
	now := time.Now()
	if l.Color {
		l.Buf.Blue()
	}
	// 输出日期的时间
	year, month, day := now.Date()
	l.Buf.AppendInt(year, 4)
	l.Buf.AppendByte('/')
	l.Buf.AppendInt(int(month), 2)
	l.Buf.AppendByte('/')
	l.Buf.AppendInt(day, 2)
	l.Buf.AppendByte(' ')
	hour, min, sec := now.Clock()
	l.Buf.AppendInt(hour, 2)
	l.Buf.AppendByte(':')
	l.Buf.AppendInt(min, 2)
	l.Buf.AppendByte(':')
	l.Buf.AppendInt(sec, 2)
	l.Buf.AppendByte(' ')
	// 如果开启颜色，则关闭颜色
	if l.Color {
		l.Buf.Off()
	}
}

// 调用染色
func (l *Logger) outputCaller(depth int, shortFile bool, showFunc bool) {
	var callerFile string  // 调用者文件名
	var callerFileLine int // 调用者行号
	var callerFunc string  // 调用者函数名
	var pc uintptr
	// 获取调用者的文件名和代码行号
	pc, callerFile, callerFileLine, _ = runtime.Caller(depth + 2)
	if shortFile {
		callerFile = filepath.Base(callerFile)
	}
	callerFunc = runtime.FuncForPC(pc).Name()
	callerFunc = callerFunc[strings.LastIndex(callerFunc, ".")+1:]

	// 染色
	if l.Color {
		l.Buf.Green()
	}
	// 打印文件名和行号
	l.Buf.Append([]byte(callerFile))
	// 显示函数名
	if showFunc {
		l.Buf.AppendByte('#')
		l.Buf.Append([]byte(callerFunc))
	}
	l.Buf.AppendByte(':')
	l.Buf.AppendInt(callerFileLine, 0)
	l.Buf.AppendByte(' ')
	// 关闭颜色
	if l.Color {
		l.Buf.Off()
	}
}

// Fatal 打印 fatal 日志，并以状态码 1 退出当前程序
func (l *Logger) Fatal(v ...interface{}) {
	if l.LogLevel <= FatalLevel {
		l.Output(l.Depth, FatalPrefix, fmt.Sprintln(v...))
	}
	os.Exit(1)
}

// Fatalf 根据指定的格式打印 fatal 日志，并以状态码 1 退出当前程序
func (l *Logger) Fatalf(format string, v ...interface{}) {
	if l.LogLevel <= FatalLevel {
		l.Output(l.Depth, FatalPrefix, fmt.Sprintf(format, v...))
	}
	os.Exit(1)
}

// Error 打印 error 日志
func (l *Logger) Error(v ...interface{}) {
	if l.LogLevel <= ErrorLevel {
		l.Output(l.Depth, ErrorPrefix, fmt.Sprintln(v...))
	}
}

// Errorf 根据指定格式打印 error 日志
func (l *Logger) Errorf(format string, v ...interface{}) {
	if l.LogLevel <= ErrorLevel {
		l.Output(l.Depth, ErrorPrefix, fmt.Sprintf(format, v...))
	}
}

// Warn 打印 warning 日志
func (l *Logger) Warn(v ...interface{}) {
	if l.LogLevel <= WarnLevel {
		l.Output(l.Depth, WarnPrefix, fmt.Sprintln(v...))
	}
}

// Warnf 根据指定格式打印 warning 日志
func (l *Logger) Warnf(format string, v ...interface{}) {
	if l.LogLevel <= WarnLevel {
		l.Output(l.Depth, WarnPrefix, fmt.Sprintf(format, v...))
	}
}

// Info 打印 info 日志
func (l *Logger) Info(v ...interface{}) {
	if l.LogLevel <= InfoLevel {
		l.Output(l.Depth, InfoPrefix, fmt.Sprintln(v...))
	}
}

// Infof 根据指定格式打印 info 日志
func (l *Logger) Infof(format string, v ...interface{}) {
	if l.LogLevel <= InfoLevel {
		l.Output(l.Depth, InfoPrefix, fmt.Sprintf(format, v...))
	}
}

// Debug 打印 debug 日志
func (l *Logger) Debug(v ...interface{}) {
	if l.LogLevel <= DebugLevel {
		l.Output(l.Depth, DebugPrefix, fmt.Sprintln(v...))
	}
}

// Debugf 根据指定格式打印 debug 日志
func (l *Logger) Debugf(format string, v ...interface{}) {
	if l.LogLevel <= DebugLevel {
		l.Output(l.Depth, DebugPrefix, fmt.Sprintf(format, v...))
	}
}

// Trace 如果开启调式模式则打印 trace 日志
func (l *Logger) Trace(v ...interface{}) {
	if l.LogLevel <= AllLevel {
		l.Output(l.Depth, TracePrefix, fmt.Sprintln(v...))
	}
}

// Tracef 如果开启调式模式则根据指定格式打印 trace 日志
func (l *Logger) Tracef(format string, v ...interface{}) {
	if l.LogLevel <= AllLevel {
		l.Output(l.Depth, TracePrefix, fmt.Sprintf(format, v...))
	}
}
